package com.example.workflow.util;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.json.JSONUtil;
import com.example.workflow.exception.ServiceException;
import com.example.workflow.flow.BizFlowTypeEnum;
import lombok.extern.slf4j.Slf4j;
import org.camunda.bpm.engine.*;
import org.camunda.bpm.engine.history.HistoricIdentityLinkLog;
import org.camunda.bpm.engine.history.HistoricProcessInstance;
import org.camunda.bpm.engine.history.HistoricTaskInstance;
import org.camunda.bpm.engine.runtime.ActivityInstance;
import org.camunda.bpm.engine.runtime.ProcessInstance;
import org.camunda.bpm.engine.runtime.ProcessInstanceModificationBuilder;
import org.camunda.bpm.engine.runtime.ProcessInstanceQuery;
import org.camunda.bpm.engine.task.IdentityLink;
import org.camunda.bpm.engine.task.Task;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.*;

/**
 * Camunda 流程工具类
 *
 */
@Component
@Slf4j
public class FlowUtil {

    private static final FlowUtil util = new FlowUtil();

    @Autowired
    private RepositoryService repositoryService;

    @Autowired
    private RuntimeService runtimeService;

    @Autowired
    private TaskService taskService;

    @Autowired
    private HistoryService historyService;

    @PostConstruct
    public void initialize() {
        util.repositoryService = repositoryService;
        util.runtimeService = runtimeService;
        util.taskService = taskService;
        util.historyService = historyService;
    }
    /**
     * 部署流程，参数本地bpmn的路径
     * @param filePath  bpmn路径
     * @param name  流程名称
     */
    public static void deployment(String filePath,String name){
        log.info("开始部署本地审批流程:{},文件地址:{}",name,filePath);
        util.repositoryService.createDeployment()
                .name(name)
                .addClasspathResource(filePath)
                .deploy();

        log.info("完成部署本地审批流程:{},文件地址:{}",name,filePath);
    }

    /**
     * 启动流程
     * @param key 流程标识,需唯一,如果存在相同key则会启动版本号最新的流程
     * @param businessKey  业务id
     * @param map 流程变量（包含节点审批人及业务判断变量等）
     */
    public static void startProcess(String key, Object businessKey, Map<String,Object> map){
        log.info("开始启动审批流程:{},业务源:{},流程变量: {}",key,businessKey.toString(), JSONUtil.toJsonStr(JSONUtil.parse(map)));
        ProcessInstance processInstance = util.runtimeService.startProcessInstanceByKey(key, businessKey.toString(), map);
        log.info("完成启动审批流程:{},业务源:{},流程实例ID:{}",key,businessKey.toString(), processInstance.getId());
    }

    /**
     * 销毁流程
     * @param key 流程标识,需唯一,如果存在相同key则会启动版本号最新的流程
     * @param businessKey  业务id
     * @param reason 销毁原因
     * @param reason 审批人
     */
    public static void destroyProcess(String key, String businessKey, String reason,String assignee){
        //查询待办任务
        List<Task> tasks = FlowUtil.getTaskByCandidateUserAndBusinessKey(key, businessKey, assignee);
        if(CollUtil.isEmpty(tasks)){
            log.info("{}完成审批任务失败:{}--{}",assignee,key,businessKey);
            throw new ServiceException("暂未到您审批");
        }
        FlowUtil.claimTask(tasks.get(0).getId(),assignee);
        List<Task> taskList = FlowUtil.getTasksByBusinessKey(key, businessKey);
        if(CollUtil.isEmpty(taskList)) return;
        log.info("开始销毁审批流程:{},业务源:{},流程实例ID:{}:",key,businessKey, taskList.get(0).getProcessInstanceId());
        util.runtimeService.deleteProcessInstance(taskList.get(0).getProcessInstanceId(), reason);
        log.info("完成销毁审批流程:{},业务源:{},流程实例ID:{}",key,businessKey, taskList.get(0).getProcessInstanceId());
    }

    /**
     * 领取任务
     * @param taskId  任务id
     * @param candidateUser  候选用户名
     */
    public static void claimTask(String taskId,String candidateUser){
        Task task = util.taskService.createTaskQuery()
                .taskId(taskId)
                .taskCandidateUser(candidateUser)
                .singleResult();
        if(Objects.isNull(task)){
            log.info("{}领取审批任务失败:{}",candidateUser,taskId);
            throw new ServiceException("任务领取失败");
        }
        util.taskService.claim(taskId,candidateUser);
    }

    /**
     * 完成任务
     * @param taskId  任务id
     * @param assignee  用户名
     */
    public static void completeTask(String taskId,String assignee){
        Task task = util.taskService.createTaskQuery()
                .taskId(taskId)
                .taskAssignee(assignee)
                .singleResult();
        if(Objects.isNull(task)){
            log.info("{}完成审批任务失败:{}",assignee,taskId);
            throw new ServiceException("任务完成失败");
        }
        util.taskService.complete(taskId);
    }

    /**
     * 领取并完成任务
     * @param taskId  任务id
     * @param assignee  用户名
     */
    public static void claimAndCompleteTask(String taskId,String assignee,Map<String,Object> map) {
        Task task = util.taskService.createTaskQuery()
                .taskId(taskId)
                .singleResult();
        if(Objects.isNull(task)){
            log.info("{} 审批任务不存在:{}",assignee,taskId);
//            throw new ServiceException("审批任务不存在");
        }

        if(StrUtil.isNotBlank(task.getAssignee())){
            if(!task.getAssignee().equalsIgnoreCase(assignee)){
                log.info("{} 完成审批任务失败:{}",assignee,taskId);
                throw new ServiceException("暂未到您审批");
            }
            util.taskService.complete(taskId);
            return;
        }
        Task unClaimTask = util.taskService.createTaskQuery()
                .taskId(taskId)
                .taskCandidateUser(assignee)
                .singleResult();
        if(Objects.isNull(unClaimTask)){
            log.info("{} 完成审批任务失败:{}",assignee,taskId);
            throw new ServiceException("暂未到您审批");
        }
        util.taskService.claim(taskId,assignee);
        if(map!=null)
        {
            util.taskService.complete(taskId, map);
        } else{
            util.taskService.complete(taskId);
        }
    }



    /**
     * 查询待办任务，参数：候选用户、业务id
     * @param candidateUser 候选用户
     * @param key 流程标识,需唯一,如果存在相同key则会启动版本号最新的流程
     * @param businessKey  业务id
     * @return
     */
    public static List<Task> getTaskByCandidateUserAndBusinessKey(String key,String businessKey,String candidateUser){
        return util.taskService.createTaskQuery()
                .processInstanceBusinessKey(businessKey)
                .processDefinitionKey(key)
                .taskCandidateUser(candidateUser)
                .list();
    }

    /**
     * 查询待办任务，参数： 业务id
     * @param key 流程标识,需唯一,如果存在相同key则会启动版本号最新的流程
     * @param businessKey  业务id
     * @return List<Task> 任务列表
     */
    public static List<Task> getTasksByBusinessKey(String key, String businessKey){
        return util.taskService.createTaskQuery()
                .processDefinitionKey(key)
                .processInstanceBusinessKey(businessKey)
                .list();
    }

    /**
     * 查询待办任务，参数： 业务id
     * @param key 流程标识,需唯一,如果存在相同key则会启动版本号最新的流程
     * @param businessKey  业务id
     * @param assignee  用户名
     * @return
     */
    public static boolean claimAndCompleteTask(String key, String businessKey,String assignee, Map<String,Object> map) {
        //查询待办任务
        List<Task> tasks = FlowUtil.getTaskByCandidateUserAndBusinessKey(key, businessKey, assignee);

        log.info("{} 有任务：{}",assignee,tasks);
        if(CollUtil.isEmpty(tasks)){
            log.info("{}完成审批任务失败:{}--{}",assignee,key,businessKey);
            throw new ServiceException("暂未到您审批");
        }
        Task task = tasks.get(0);
        FlowUtil.claimAndCompleteTask(task.getId(),assignee,map);
//        for (Task task : tasks) {
//
//            FlowUtil.claimAndCompleteTask(task.getId(),assignee, map);
//        }
        if(CollUtil.isEmpty(FlowUtil.getTasksByBusinessKey(key,businessKey))) {
            log.info("{}完成审批任务:{}--{}",assignee,key,businessKey);
            return true;
        } else {
            return false;
        }
    }


    /**
     * 查询历史任务
     * @param key
     * @param businessKey
     * @return
     */
    public static List<HistoricTaskInstance> getHistoricTask(String processInstanceId,String key, String businessKey){
        return util.historyService.createHistoricTaskInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .processInstanceId(processInstanceId)
                .processDefinitionKey(key)
                .list();
    }

    /**
     * 查询历史实例
     * @param key
     * @param businessKey
     * @return
     */
    public static HistoricProcessInstance getLastProcessInstance(String key, String businessKey){
        return util.historyService.createHistoricProcessInstanceQuery()
                .processDefinitionKey(key)
                .processInstanceBusinessKey(businessKey)
                .orderByProcessInstanceStartTime()
                .desc().list().get(0);
    }

    /**
     * 查询任务
     * @param key
     * @param taskId
     * @return
     */
    public static List<HistoricIdentityLinkLog> getHistoricIdentityLinkLog(String key, String taskId){
        return util.historyService.createHistoricIdentityLinkLogQuery()
                .processDefinitionKey(key)
                .taskId(taskId)
                .list();
    }


    /**
     * 查询任务
     * @param taskId 任务ID
     * @return
     */
    public static List<IdentityLink> getTaskIdentityLink(String taskId){
        return util.taskService.getIdentityLinksForTask(taskId);
    }


    public static void addSign(String key,String businessKey,String nodeId,String activityId) {
// 查询符合条件的流程实例
        List<ProcessInstance> processInstances = util.runtimeService.createProcessInstanceQuery()
                .processDefinitionKey(key) // 指定流程定义的 key
                .processInstanceBusinessKey(businessKey)    // 指定业务键
                .list();

        if (processInstances.isEmpty()) {
            log.info("未找到符合条件的流程实例");
            throw new ServiceException("未找到符合条件的流程实例");
        }

        if (processInstances.size() > 1) {
            log.info("找到多个符合条件的流程实例，返回第一个");
        }
        String processInstanceId = processInstances.get(0).getId();
        // 子流程节点的Id，在流程图中查看，此为固定值（一般起一个正规的名字，本例是自动生成的随机名）
//        String activityId = "Activity_0qs5wc1";

        util.runtimeService.createProcessInstanceModification(processInstanceId)
                .startAfterActivity(activityId)
                .startBeforeActivity(nodeId) // 子流程Id，在流程图中看，固定值（一般起一个正规的名字）
                .setVariable("带入子流程的变量key","变量值Object类型")
                .execute();

        log.info("增加子流程实例成功");
    }

    /**
     * 删除正在运行的流程实例
     *
     * @param processId processInstanceId 流程实例ID
     * @param reason 删除理由
     */
    public static void deleteProcessInstance(String processId,String reason) {
        List<String> list = List.of("b122c91d-318a-11f0-876c-00155d440100");
        for(String processInstanceId : list)
        {
            util.runtimeService.deleteProcessInstance(processInstanceId, reason);
            util.historyService.deleteHistoricProcessInstance(processInstanceId);
        }
    }

    /**
     * 给新注册用户分配当前工单权限
     *
     * @param businessKey 业务键
     * @param role 角色
     * @param userId 用户ID
     */
    public static void updateVariable(String key,String businessKey,String role,Integer userId) {
        final int MAX_RETRIES = 3;
        int retryCount = 0;
        
        while (retryCount < MAX_RETRIES) {
            try {
                ProcessInstanceQuery query = util.runtimeService.createProcessInstanceQuery()
                        .processDefinitionKey(key)
                        .processInstanceBusinessKey(businessKey);

                List<ProcessInstance> processInstances = query.list();
                if (processInstances == null || processInstances.isEmpty()) {
                    log.warn("未找到业务键为{}的流程实例，跳过权限分配", businessKey);
                    return;
                }
                if (processInstances.size() > 1) {
                    log.warn("找到多个业务键为{}的流程实例，使用最新的实例", businessKey);
                }
                ProcessInstance processInstance = processInstances.get(0);
                String processInstanceId = processInstance.getId();

                List<Task> tasks = util.taskService.createTaskQuery()
                        .processInstanceId(processInstanceId)
                        .list();

                if(tasks != null && !tasks.isEmpty()) {
                    Task currentTask = tasks.get(0);
                    String taskId = currentTask.getId();
                    switch (role) {
                        case "CITY" -> {
                            Set<String> existingSet = (Set<String>) util.runtimeService.getVariable(processInstanceId, "cityUsers");
                            if (existingSet == null) {
                                existingSet = new HashSet<>();
                            }
                            existingSet.add(userId.toString());
                            util.runtimeService.setVariable(processInstanceId, "cityUsers", existingSet);
                            addCandidateUser(processInstanceId, String.valueOf(userId), role);
                        }
                        case "PROVINCE" -> {
                            Set<String> existingSet = (Set<String>) util.runtimeService.getVariable(processInstanceId, "provinceUsers");
                            if (existingSet == null) {
                                existingSet = new HashSet<>();
                            }
                            existingSet.add(userId.toString());
                            util.runtimeService.setVariable(processInstanceId, "provinceUsers", existingSet);
                            addCandidateUser(processInstanceId, String.valueOf(userId), role);
                        }
                        case "OPERATOR" -> {
                            Set<String> existingSet = (Set<String>) util.runtimeService.getVariable(processInstanceId, "operatorUsers");
                            Set<String> helpSet = (Set<String>) util.runtimeService.getVariable(processInstanceId,"helpUsers");
                            if (existingSet == null) {
                                existingSet = new HashSet<>();
                            }
                            if(helpSet == null) {
                                helpSet = new HashSet<>();
                            }
                            existingSet.add(userId.toString());
                            util.runtimeService.setVariable(processInstanceId, "operatorUsers", existingSet);

                            helpSet.add(userId.toString());
                            util.runtimeService.setVariable(processInstanceId, "helpUsers", helpSet);
                            addCandidateUser(processInstanceId, String.valueOf(userId), role);
                        }
                    }
                }
                return; // 成功执行后返回
            } catch (org.camunda.bpm.engine.OptimisticLockingException e) {
                retryCount++;
                if (retryCount >= MAX_RETRIES) {
                    log.error("更新流程变量失败，已达到最大重试次数{}: {}", MAX_RETRIES, e.getMessage());
                    throw e;
                }
                log.warn("并发更新冲突，正在进行第{}次重试...", retryCount);
                try {
                    Thread.sleep(100 * retryCount); // 指数退避
                } catch (InterruptedException ie) {
                    Thread.currentThread().interrupt();
                    throw new RuntimeException("重试被中断", ie);
                }
            }
        }
    }

    public static void addCandidateUser(String processInstanceId,String newUserId,String role) {
        // 获取当前流程实例的活动实例树
        ActivityInstance instance = util.runtimeService.getActivityInstance(processInstanceId);
//        for (ActivityInstance child : instance.getChildActivityInstances()) {
//            findMultiInstanceActivities(child);
//        }
        if (instance != null) {
            ActivityInstance parent = instance.getChildActivityInstances()[0];
            ActivityInstance child = parent.getChildActivityInstances()[0];
             System.out.println("当前运行的类型 ："+child.getActivityType());
             System.out.println("当前运行的ID："+child.getActivityId());
            if (role.equals("CITY") && child.getActivityId().equals(BizFlowTypeEnum.FIRST_APPROVE.getKey())) {
                 System.out.println("city add ");
                startNewInstance(processInstanceId,newUserId,child.getActivityId());
            } else if(role.equals("PROVINCE")&&child.getActivityId().equals(BizFlowTypeEnum.SECOND_APPROVE.getKey())) {
                 System.out.println("province add ");
                startNewInstance(processInstanceId,newUserId,child.getActivityId());
            } else if(role.equals("OPERATOR")&&(
                    child.getActivityId().equals(BizFlowTypeEnum.HELP_ORDER.getKey())
                    ||child.getActivityId().equals(BizFlowTypeEnum.OPERATE_ORDER.getKey())
                    ||child.getActivityId().equals(BizFlowTypeEnum.DISPATCH_ORDER.getKey())
                    ||child.getActivityId().equals(BizFlowTypeEnum.MOPERATE_ORDER.getKey()))) {
                 System.out.println("operator add ");
                startNewInstance(processInstanceId,newUserId,child.getActivityId());
            }

        }
        return;
    }

    /**
     * 重新启动一次流程（？）
     *
     * @param processInstanceId
     * @param newUserId
     * @param activityId
     */
    public static void startNewInstance(String processInstanceId,String newUserId,String activityId) {
        // 查询当前任务
        List<Task> tasks = util.taskService.createTaskQuery()
                .processInstanceId(processInstanceId)
                .list();
        for (Task currentTask : tasks)
        {
            if (currentTask != null) {
                // 添加新的候选人
                String taskId = currentTask.getId();
                util.taskService.addCandidateUser(taskId, newUserId);
//                // System.out.println("addCandidateUsers : " + newUserId);
            }
        }
        // 重新触发多实例任务
        ProcessInstanceModificationBuilder modificationBuilder = util.runtimeService.createProcessInstanceModification(processInstanceId);
        modificationBuilder.startBeforeActivity(activityId); // 替换为实际的用户任务 ID
        modificationBuilder.execute();
    }

    /**
     * 查看流程的全局参数
     *
     * @param businessKey
     * @param variableName
     */
    public static void printVariable(String businessKey,String variableName) {
        ProcessInstance instance = util.runtimeService.createProcessInstanceQuery()
                .processInstanceBusinessKey(businessKey)
                .singleResult();
        if (instance == null) {
            log.warn("未找到业务键为{}的流程实例，跳过权限分配", businessKey);
            return;
        }
        String executionId = instance.getId();
        // System.out.println("variable  "+util.runtimeService.getVariable(executionId,variableName));

    }
}